/*
	Staff Weapon for GarrysMod10
	Copyright (C) 2007  aVoN

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

--################# HEADER #################
AddCSLuaFile("cl_init.lua");
AddCSLuaFile("shared.lua");
include("shared.lua");
ENT.Untouchable = true;
ENT.IgnoreTouch = true;
ENT.NoAutoClose = true; -- Will not cause an autoclose event on the stargates!
ENT.CDSIgnore = true; -- CDS Immunity
function ENT:gcbt_breakactions() end; ENT.hasdamagecase = true; -- GCombat invulnarability!

--################# SENT CODE ###############

--################### Init @aVoN
function ENT:Initialize()
	self.Entity:PhysicsInitSphere(10,"metal");
	self.Entity:SetCollisionBounds(Vector()*-5,Vector()*5);
	self.Entity:SetCollisionGroup(COLLISION_GROUP_PROJECTILE);
	self.Entity:SetMoveType(MOVETYPE_FLY);
	self.Entity:DrawShadow(false);
	-- Config
	self.Radius = StarGate.CFG:Get("staff","radius",50);
	self.Damage = StarGate.CFG:Get("staff","damage",150);
	self.MaxPasses = StarGate.CFG:Get("staff","maxpasses",5);
	if(self.ColorizeExplosion == nil) then self.ColorizeExplosion = true end;
	self.Passes = 1;
	self.Passed = {}; -- Necessary, so you can shoot out of Catdaemons shield
	local r,g,b = self.Entity:GetColor();
	if(r == 255 and g == 255 and b == 255) then
		self.Entity:SetColor(math.random(230,255),200,120,255);
	end
	self.Effects = {Hit="staff_impact"};
	-- PhysObject
	self.Phys = self.Entity:GetPhysicsObject();
	if(self.Phys and self.Phys:IsValid()) then
		self.Phys:SetMass(20);
	end
end

--################# Prevent PVS bug/drop of all networkes vars (Let's hope, it works) @aVoN
function ENT:UpdateTransmitState() return TRANSMIT_ALWAYS end;

--################### Make the shot blast @aVoN
function ENT:Touch(e)
	if(e and e.IgnoreTouch) then return end; -- Gloabal for anyone, who want's to make his scripts "staff-passable"
	if(self.Passed[e]) then return end; -- We already passed this entity. Don't touch it again
	local valid = ValidEntity(e);
	local world = false;
	if(e and e.IsWorld) then world = e:IsWorld() end;
	if(not world and valid and self.Passed[e] == nil) then
		-- Invalid physics object?
		if(not e:GetPhysicsObject():IsValid()) then self.Passed[e] = true return end;
		-- Catdaemon's cloaking device
		if(e:GetClass() == "ivisgen_collision") then self.Passed[e] = true return end;
		-- Catdaemon's and my shield?
		if(e and e.NoCollide) then
			local owner = self.Entity:GetOwner();
			if(e.NoCollide[owner]) then
				e.NoCollide[e] = true;
				self.Passed[e] = true;
				return;
			end
			self.Passed[e] = false;
		end
	end
	-- ######################## Time to remove the pulse?
	local pos = self.Entity:GetPos();
	if(not self.Velocity) then self.Velocity = self.Entity:GetVelocity() end; -- Failsafe!
	local vel = self.Velocity:GetNormalized()*140;
	local hitsmoke = true;
	local owner = self.Entity:GetOwner();
	local trace = util.TraceLine({start=pos-vel,endpos=pos+vel,filter={self.Entity,owner}});
	if(trace.HitSky) then self.Entity:Remove() return end;
	if(trace and trace.HitNormal:Length() == 0) then hitsmoke = false end;
	if(self.LastHit == e or self.Passes >= self.MaxPasses or (world and self.Effects.Explode)) then
		if(self.Effects.Explode) then -- It's fired from the SENT - Do the explosion hiteffect
			if(ValidEntity(self.Cannon)) then self.Cannon.Shots[self.Entity] = nil end; -- Tell our cannon, we are killing ourself now
			if(ValidEntity(owner) and owner.DrawExplosion) then
				self:Blast(self.Effects.Explode,pos,e,trace.HitNormal,hitsmoke,self.ExplosiveDamage,self.ExplosiveRadius);
			else
				-- Do a normal staff-inpact, but with high damage
				self:Blast(self.Effects.Hit,pos,e,trace.HitNormal,hitsmoke,self.ExplosiveDamage,self.ExplosiveRadius);
			end
		end
		self:Destroy();
		return;
	end
	-- ######################## The blast
	if(trace.MatType == MAT_FLESH or trace.MatType == MAT_METAL or trace.MatType == MAT_GLASS) then hitsmoke = false end;
	self:Blast("staff_impact",pos,e,trace.HitNormal,hitsmoke,self.Damage,self.Radius);
	-- ######################## For go-through - But not on shields (mine or Catdaemons)
	self.LastHit = e;
	if(self.Velocity and not world and not (e and e.NoCollide)) then
		-- When hit something, the velocity changes normally
		self.Entity:SetPos(self.Entity:GetPos()-vel:Normalize()*10);
		self.Entity:SetVelocity(self.Velocity);
		-- Some short units behind the prop again, so the hsot knows, it either hit and destroyed the prop (fly through) or not and will explode/end now on it
		self.Passes = (self.Passes or 1)+1;
	else
		self:Destroy();
	end
end

-- ######################## Damage system
function ENT:Blast(effect,pos,ent,norm,smoke,dmg,rad)
	local fx = EffectData();
	fx:SetOrigin(pos);
	fx:SetNormal(norm);
	fx:SetEntity(ent);
	if(not smoke) then
		fx:SetScale(-1);
	else
		fx:SetScale(1);
	end
	-- How are we colorized?
	if(self.ColorizeExplosion) then
		fx:SetAngle(Angle(self.Entity:GetColor()));
	else
		fx:SetAngle(Angle(0,0,0));
	end
	util.Effect(effect,fx,true,true);
	local i = 1/math.sqrt(self.Passes); -- Intensity
	if(self.ExplosiveDamage) then
		dmg = math.Clamp(self.ExplosiveDamage*i,0,dmg); -- Necessary, or the powerfull shot won't go throug breakable stuff such good anymore
	end
	local attacker,owner = StarGate.GetAttackerAndOwner(self.Entity);
	StarGate.BlastDamage(attacker,owner,pos,rad,dmg*i);
	util.ScreenShake(pos,2,2.5,1,700);
end

--################### I'm thinking, I'm thinking @aVoN
function ENT:Think()
	local time = CurTime();
	--if(self.Created + 0.1 < time) then return end; -- Wait until we got full speed
	local vel = self.Entity:GetVelocity();
	if(not self.Velocity) then self.Velocity = vel end; -- No need to tell "Velocity" to the blast anymore by the cannon! Yehaw!
	-- ######################## Delete, when too slow
	if(vel:Length() < 1000) then
		-- Delete it after a delay - I think, this was the reason why it crashed servers when shot through a gate
		self:Destroy();
		self.Entity:NextThink(time + 10); -- Stops doing this here again
		return true;
	end
	self.Entity:NextThink(time + 0.3);
	return true;
end

--################### Destroys this entity without fearing to crash! @aVoN
function ENT:Destroy()
	self.Touch = function() end; -- Dummy
	self.StartTouch = self.Touch;
	self.EndTouch = self.Touch;
	self:SetTrigger(false);
	local e = self.Entity;
	timer.Simple(0,
		function()
			if(ValidEntity(e)) then e:Remove() end;
		end
	);
end

--################### Earthquake! @aVoN
concommand.Add("_StarGate.StaffBlast.ScreenShake",
	function(p,_,arg)
		if(ValidEntity(p)) then
			util.ScreenShake(Vector(unpack(arg)),2,2.5,1,700);
		end
	end
);